package com.onlyxiahui.common.utils.base.beans;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.onlyxiahui.common.utils.base.lang.reflect.ClassUtil;
import com.onlyxiahui.common.utils.base.lang.reflect.PropertyUtil;

/**
 * 
 * Description <br>
 * Date 2020-11-12 15:20:09<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class BeanDefaultValueUtil {

	public interface DefaultValueCreator {
		<T> T getDefaultValue(Class<T> clazz);
	}

	protected static final Map<Class<?>, DefaultValueCreator> DEFAULT_VALUE_CREATOR_MAP = new HashMap<>();
	protected static final Map<Object, List<PropertyDescriptor>> METHOD_LIST_MAP = new HashMap<>();

	public static void putDefaultValueCreator(Class<?> clazz, DefaultValueCreator defaultValueCreator) {
		DEFAULT_VALUE_CREATOR_MAP.put(clazz, defaultValueCreator);
	}

	public static void setSimpleDefaultValue(Object o, DefaultValueCreator creator) {

		if (null != o) {
			Class<?> classType = o.getClass();
			List<PropertyDescriptor> propertyDescriptorList = getMethodPropertyDescriptorList(classType);
			for (PropertyDescriptor pd : propertyDescriptorList) {

				Method readMethod = pd.getReadMethod();

				if (null != readMethod) {
					try {
						Object v = readMethod.invoke(o);
						if (null == v) {
							Method writeMethod = pd.getWriteMethod();
							Class<?> type = pd.getPropertyType();
							Object value = getDefaultValue(type, creator);
							if (null == value && ClassUtil.isCanInstance(type)) {
								try {
									value = type.newInstance();
								} catch (InstantiationException | IllegalAccessException e) {
									e.printStackTrace();
								}
							}
							if (null != value) {
								writeMethod.invoke(o, value);
							}
						}
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	public static void setSimpleDefaultValue(Object o) {
		setSimpleDefaultValue(o, null);
	}

	public static void setDeepnDefaultValue(Object o, DefaultValueCreator creator) {
		if (null != o) {
			Class<?> classType = o.getClass();
			List<PropertyDescriptor> propertyDescriptorList = getMethodPropertyDescriptorList(classType);
			for (PropertyDescriptor pd : propertyDescriptorList) {

				Method readMethod = pd.getReadMethod();

				if (null != readMethod) {
					try {
						Object v = readMethod.invoke(o);
						if (null == v) {
							Method writeMethod = pd.getWriteMethod();
							Class<?> type = pd.getPropertyType();
							Object value = getDefaultValue(type, creator);
							if (null == value && ClassUtil.isCanInstance(type)) {
								try {
									value = type.newInstance();
									setDeepnDefaultValue(value, creator);
								} catch (InstantiationException | IllegalAccessException e) {
									e.printStackTrace();
								}
							}
							if (null != value) {
								writeMethod.invoke(o, value);
							}
						}
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	public static void setDeepnDefaultValue(Object o) {
		setDeepnDefaultValue(o, null);
	}

	@SuppressWarnings("unchecked")
	public static <T> T getDefaultValue(Class<T> clazz, DefaultValueCreator creator) {
		Object object = null;
		if (null != creator) {
			object = creator.getDefaultValue(clazz);
		}

		if (null == object) {
			if (List.class.isAssignableFrom(clazz)) {
				object = new ArrayList<Object>(0);
			} else if (Map.class.isAssignableFrom(clazz)) {
				object = new HashMap<Object, Object>(0);
			} else if (Set.class.isAssignableFrom(clazz)) {
				object = new HashSet<Object>(0);
			} else if (clazz.isArray()) {
				Class<?> componentType = clazz.getComponentType();
				object = Array.newInstance(componentType, 0);
			} else if (BigDecimal.class.isAssignableFrom(clazz)) {
				object = new BigDecimal(0);
			} else if (BigInteger.class.isAssignableFrom(clazz)) {
				object = new BigInteger("0");
			} else if (Date.class.isAssignableFrom(clazz)) {
				object = new Date();
			} else if (LocalDate.class.isAssignableFrom(clazz)) {
				object = LocalDate.now();
			} else if (LocalDateTime.class.isAssignableFrom(clazz)) {
				object = LocalDateTime.now();
			} else if (Timestamp.class.isAssignableFrom(clazz)) {
				object = new Timestamp(System.currentTimeMillis());
			} else if (PropertyUtil.isPrimitive(clazz)) {
				object = PropertyUtil.getDefaultValue(clazz);
			}
		}

		if (null == object) {
			DefaultValueCreator cc = DEFAULT_VALUE_CREATOR_MAP.get(clazz);
			if (null != cc) {
				object = cc.getDefaultValue(clazz);
			}
		}
		return ((T) object);
	}

	/**
	 * 获取类中有set方法是属性
	 * 
	 * @param classType
	 * @return
	 */
	public static List<PropertyDescriptor> getMethodPropertyDescriptorList(Class<?> classType) {
		List<PropertyDescriptor> propertyDescriptorList = METHOD_LIST_MAP.get(classType);
		try {
			if (null == propertyDescriptorList) {
				propertyDescriptorList = new ArrayList<>();
				BeanInfo bi = Introspector.getBeanInfo(classType, Object.class);
				PropertyDescriptor[] pds = bi.getPropertyDescriptors();
				for (PropertyDescriptor pd : pds) {
					propertyDescriptorList.add(pd);
				}
				METHOD_LIST_MAP.put(classType, propertyDescriptorList);
			}
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}
		return propertyDescriptorList;
	}
}
